Skip to content

Add 0084 orphan fallback (both observers)#241

Merged
chris-colinsky merged 3 commits into
mainfrom
feature/0084-orphan-fallback
Jul 27, 2026
Merged

Add 0084 orphan fallback (both observers)#241
chris-colinsky merged 3 commits into
mainfrom
feature/0084-orphan-fallback

Conversation

@chris-colinsky

Copy link
Copy Markdown
Member

Second and final observer-side PR for proposal 0084 (nested-fan-out span lineage), completing the work started in #240. Under the current spec pin, no bump.

What

  • Orphan fallback, both observers. When a provider call (or a framework failure-isolation marker) is issued with no open calling-node span — a guardrail or middleware side-call rather than the node body — its span/observation now parents under the nearest enclosing wrapper on the calling node's lineage, chain-routed to the correct inner fan-out instance rather than a coincidentally-indexed sibling or a top-level shortcut. A shared resolver (_resolve_enclosing_wrapper_context on OTel, _resolve_enclosing_wrapper_observation_id on Langfuse) backs both the node parent and the orphan fallback, so an in-body call and a wrapper call on the same lineage resolve to the same parent. FailureIsolatedEvent parenting now routes through it too, so the marker resolves consistently across the two observers.
  • Langfuse node-observation key gains the enclosing lineage chains (mirroring the OTel node-span key from Add 0084 event lineage chains and OTel exact-match #240), so nested exact-match works on the Langfuse side under concurrency.
  • Conformance. Un-defers observability fixtures 133 (OTel orphan) and 134 (Langfuse: nested exact-match + orphan). The calls_llm_from_wrapper orphan-call primitive is modeled on NodeSpec, so 133/134 both parse/round-trip and run.

Known limitation (documented, tracked)

A wrapper call issued in the pre-phase (before the wrapped node body) inside a nested fan-out resolves to the outer instance in Langfuse — which creates instance observations lazily — while OTel resolves to the inner instance. That's a cross-observer parity gap for the pre-phase scenario, which the fixtures do not cover (the harness exercises the orphan path via post-phase instance middleware, where the two agree; 134 asserts that agreement with a live OTel-vs-Langfuse parent comparison). The conformance.toml 0084 note documents it, and the Langfuse eager-instance-observation fix plus the spec question are tracked as follow-ups.

Review

This PR went through a multi-lens adversarial review, which surfaced the pre-phase parity gap above (previously masked), a cross-observer asymmetry in failure-isolation parenting (fixed here), and a tautological parity assertion in fixture 134 (replaced with a real comparison).

Testing

pytest tests/ — 1776 passed. ruff + pyright clean.

Proposal 0084 (nested-fan-out span lineage), second of the Wave 3 PRs;
completes the observer-side work started in #240.

Generalize the observability orphan fallback -- a provider or framework
call with no open calling-node span (a guardrail or middleware side
call) -- in both the OTel and Langfuse observers to resolve the nearest
enclosing wrapper on the calling node's lineage, chain-routed to the
correct inner fan-out instance. A shared resolver backs both the node
parent and the orphan fallback in each observer, and now also backs
FailureIsolatedEvent parenting so the marker resolves consistently
across observers. The Langfuse node-observation key gains the enclosing
lineage chains (mirroring the OTel node-span key from #240) so nested
exact-match works there too.

Un-defer observability fixtures 133 (OTel orphan) and 134 (Langfuse,
exact-match + orphan). The orphan-call primitive calls_llm_from_wrapper
is modeled on NodeSpec so 133 and 134 both parse and run.

Known limitation: a wrapper call issued before the wrapped node body
(pre-phase) inside a nested fan-out resolves to the outer instance in
Langfuse, which creates instance observations lazily, while OTel
resolves to the inner instance -- a cross-observer parity gap the
fixtures do not cover. The conformance.toml 0084 note documents it;
the Langfuse eager-creation fix and the spec question are tracked as
follow-ups.
Copilot AI review requested due to automatic review settings July 27, 2026 00:00
Comment thread tests/conformance/test_observability_langfuse.py Fixed
Comment thread tests/conformance/test_observability_langfuse.py Fixed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes the observer-side implementation of proposal 0084 (nested fan-out span/observation lineage) by adding a chain-aware “orphan fallback” parent resolver in both OTel and Langfuse, updating Langfuse node observation keying to include lineage chains, and enabling the previously deferred conformance fixtures (133/134) via a new conformance adapter directive for wrapper-issued LLM calls.

Changes:

  • Implement shared chain-aware “nearest enclosing wrapper” resolution for orphaned provider calls (and align FailureIsolatedEvent parenting via the same resolver).
  • Extend Langfuse in-flight node-observation keying with lineage chains to avoid collisions under nested concurrency.
  • Un-defer and wire new conformance harness runners and directive parsing for wrapper-issued (orphan) LLM calls.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/unit/test_observability_otel.py Updates unit coverage for OTel orphan fallback to include lineage chains.
tests/unit/test_observability_langfuse.py Updates Langfuse unit coverage for lineage-aware node keys and orphan fallback.
tests/conformance/test_observability.py Enables fixture 133 and adds a dedicated runner to exercise nested orphan fallback in OTel.
tests/conformance/test_observability_langfuse.py Enables fixture 134 with a dedicated runner comparing Langfuse vs OTel resolved parents.
tests/conformance/test_fixture_parsing.py Removes parsing deferrals for 133/134 now that the directive is modeled and runnable.
tests/conformance/harness/directives.py Adds CallsLlmFromWrapperSpec and NodeSpec.calls_llm_from_wrapper (additive directive).
src/openarmature/observability/otel/observer.py Replaces ad-hoc fallbacks with a shared chain-aware enclosing-wrapper parent resolver.
src/openarmature/observability/langfuse/observer.py Adds lineage chains to node keys; introduces shared enclosing-wrapper resolver; updates FailureIsolatedEvent parenting.
src/openarmature/graph/events.py Updates FailureIsolatedEvent documentation to reflect §5.5 orphan fallback parenting.
conformance.toml Marks proposal 0084 as implemented and documents the known Langfuse pre-phase parity limitation.

Comment thread src/openarmature/observability/otel/observer.py
Add unit-test guards pinning the branch-side of the §5.5 orphan fallback
in both observers -- an orphan provider call inside a parallel branch
parents under the per-branch dispatch span / observation (the nearest
enclosing wrapper), the branch analog of the fan-out orphan tests. The
0084 fixtures cover fan-out nesting only; a spec fixture for the branch
case is flagged for the batched review (release-v0.17.0 coord).
@chris-colinsky
chris-colinsky merged commit df862a0 into main Jul 27, 2026
6 checks passed
@chris-colinsky
chris-colinsky deleted the feature/0084-orphan-fallback branch July 27, 2026 02:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants